"""
基于参考项目的FastAPI邮件服务器
严格按照 OutlookManager-main/main.py 实现
"""

import asyncio
import email as email_module
import imaplib
import json
import logging
import socket
import time
from datetime import datetime, timedelta
from typing import List, Optional
import hashlib

import httpx
from email.header import decode_header
from email.utils import parsedate_to_datetime
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
import io

# 配置常量（与参考项目一致）
TOKEN_URL = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"
OAUTH_SCOPE = "https://outlook.office.com/IMAP.AccessAsUser.All offline_access"
IMAP_SERVER = "outlook.live.com"
IMAP_PORT = 993
CONNECTION_TIMEOUT = 30
SOCKET_TIMEOUT = 15

# 日志配置
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 全局缓存系统
email_cache = {}
token_cache = {}
email_list_cache = {}  # 邮件列表缓存
imap_connection_pool = {}
cache_expire_time = 300  # 5分钟缓存
list_cache_expire_time = 180  # 3分钟邮件列表缓存
token_expire_time = 3300  # 55分钟Token缓存（Token有效期1小时）

def get_cache_key(email: str, message_id: str) -> str:
    """生成缓存键"""
    return hashlib.md5(f"{email}:{message_id}".encode()).hexdigest()

def get_cached_email(email: str, message_id: str) -> Optional[dict]:
    """获取缓存的邮件"""
    cache_key = get_cache_key(email, message_id)
    if cache_key in email_cache:
        cached_data, timestamp = email_cache[cache_key]
        if datetime.now() - timestamp < timedelta(seconds=cache_expire_time):
            logger.info(f"Using cached email for {message_id}")
            return cached_data
        else:
            del email_cache[cache_key]
    return None

def cache_email(email: str, message_id: str, data: dict):
    """缓存邮件数据"""
    cache_key = get_cache_key(email, message_id)
    email_cache[cache_key] = (data, datetime.now())
    logger.info(f"Cached email {message_id}")

# Token缓存系统
def get_token_cache_key(client_id: str, refresh_token: str) -> str:
    """生成Token缓存键"""
    return hashlib.md5(f"{client_id}:{refresh_token}".encode()).hexdigest()

def get_cached_token(client_id: str, refresh_token: str) -> Optional[str]:
    """获取缓存的Token"""
    cache_key = get_token_cache_key(client_id, refresh_token)
    if cache_key in token_cache:
        cached_token, timestamp = token_cache[cache_key]
        if datetime.now() - timestamp < timedelta(seconds=token_expire_time):
            logger.info("Using cached access token")
            return cached_token
        else:
            del token_cache[cache_key]
    return None

def cache_token(client_id: str, refresh_token: str, token: str):
    """缓存Token"""
    cache_key = get_token_cache_key(client_id, refresh_token)
    token_cache[cache_key] = (token, datetime.now())
    logger.info("Cached access token")

# 邮件列表缓存系统
def get_email_list_cache_key(email: str, top: int) -> str:
    """生成邮件列表缓存键"""
    return hashlib.md5(f"{email}:list:{top}".encode()).hexdigest()

def get_cached_email_list(email: str, top: int) -> Optional[List]:
    """获取缓存的邮件列表"""
    cache_key = get_email_list_cache_key(email, top)
    if cache_key in email_list_cache:
        cached_list, timestamp = email_list_cache[cache_key]
        if datetime.now() - timestamp < timedelta(seconds=list_cache_expire_time):
            logger.info(f"Using cached email list for {email}")
            return cached_list
        else:
            del email_list_cache[cache_key]
    return None

def cache_email_list(email: str, top: int, email_list: List):
    """缓存邮件列表"""
    cache_key = get_email_list_cache_key(email, top)
    email_list_cache[cache_key] = (email_list, datetime.now())
    logger.info(f"Cached email list for {email} ({len(email_list)} emails)")

# 邮件列表缓存系统
def get_email_list_cache_key(email: str, top: int) -> str:
    """生成邮件列表缓存键"""
    return hashlib.md5(f"{email}:list:{top}".encode()).hexdigest()

def get_cached_email_list(email: str, top: int) -> Optional[List]:
    """获取缓存的邮件列表"""
    cache_key = get_email_list_cache_key(email, top)
    if cache_key in email_list_cache:
        cached_list, timestamp = email_list_cache[cache_key]
        if datetime.now() - timestamp < timedelta(seconds=list_cache_expire_time):
            logger.info(f"Using cached email list for {email}")
            return cached_list
        else:
            del email_list_cache[cache_key]
    return None

def cache_email_list(email: str, top: int, email_list: List):
    """缓存邮件列表"""
    cache_key = get_email_list_cache_key(email, top)
    email_list_cache[cache_key] = (email_list, datetime.now())
    logger.info(f"Cached email list for {email} ({len(email_list)} emails)")

# IMAP连接池
def get_connection_pool_key(email: str) -> str:
    """生成连接池键"""
    return email

def get_pooled_connection(email: str, access_token: str):
    """获取池化的IMAP连接"""
    pool_key = get_connection_pool_key(email)

    if pool_key in imap_connection_pool:
        connection, timestamp = imap_connection_pool[pool_key]
        # 检查连接是否仍然有效（10分钟内）
        if datetime.now() - timestamp < timedelta(minutes=10):
            try:
                # 测试连接是否仍然活跃
                connection.noop()
                logger.info(f"Reusing IMAP connection for {email}")
                return connection
            except:
                # 连接已断开，从池中移除
                del imap_connection_pool[pool_key]

    # 创建新连接
    connection = create_imap_connection(email, access_token)
    imap_connection_pool[pool_key] = (connection, datetime.now())
    logger.info(f"Created new pooled IMAP connection for {email}")
    return connection

def cleanup_expired_connections():
    """清理过期的连接和缓存"""
    current_time = datetime.now()

    # 清理过期的IMAP连接
    expired_connection_keys = []
    for pool_key, (connection, timestamp) in imap_connection_pool.items():
        if current_time - timestamp > timedelta(minutes=15):  # 15分钟后清理
            expired_connection_keys.append(pool_key)
            try:
                connection.logout()
            except:
                pass

    for key in expired_connection_keys:
        del imap_connection_pool[key]
        logger.info(f"Cleaned up expired IMAP connection: {key}")

    # 清理过期的邮件列表缓存
    expired_list_keys = []
    for cache_key, (_, timestamp) in email_list_cache.items():
        if current_time - timestamp > timedelta(seconds=list_cache_expire_time):
            expired_list_keys.append(cache_key)

    for key in expired_list_keys:
        del email_list_cache[key]
        logger.info(f"Cleaned up expired email list cache: {key}")

    # 清理过期的邮件详情缓存
    expired_email_keys = []
    for cache_key, (_, timestamp) in email_cache.items():
        if current_time - timestamp > timedelta(seconds=cache_expire_time):
            expired_email_keys.append(cache_key)

    for key in expired_email_keys:
        del email_cache[key]
        logger.info(f"Cleaned up expired email cache: {key}")

# 定期清理过期连接
import threading
def periodic_cleanup():
    cleanup_expired_connections()
    # 每5分钟清理一次
    threading.Timer(300, periodic_cleanup).start()

# 启动定期清理
periodic_cleanup()

# 数据模型
class TempMessageRequest(BaseModel):
    email: str
    client_id: str
    refresh_token: str
    top: Optional[int] = 20

class TempMessageDetailRequest(BaseModel):
    email: str
    client_id: str
    refresh_token: str
    message_id: str


class EmailMessage(BaseModel):
    id: str
    subject: str
    receivedDateTime: str
    sender: dict
    from_: dict = None
    body: dict
    bodyPreview: str

    model_config = {"populate_by_name": True}

    def model_dump(self, **kwargs):
        data = super().model_dump(**kwargs)
        if 'from_' in data:
            data['from'] = data.pop('from_')
        return data

class TempMessageResponse(BaseModel):
    success: bool
    message: str
    data: List[EmailMessage]

class EmailDetailResponse(BaseModel):
    success: bool
    message: str
    data: dict

# FastAPI应用
app = FastAPI(title="临时邮箱API服务")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 不使用mount，改用特定路由处理静态文件

def format_datetime(iso_string: str) -> str:
    """格式化日期时间"""
    try:
        date = datetime.fromisoformat(iso_string.replace('Z', '+00:00'))
        return date.strftime('%Y-%m-%d %H:%M:%S')
    except:
        return iso_string

def decode_header_value(header_value: str) -> str:
    """解码邮件头字段"""
    if not header_value:
        return ""

    try:
        decoded_parts = decode_header(str(header_value))
        decoded_string = ""

        for part, charset in decoded_parts:
            if isinstance(part, bytes):
                try:
                    encoding = charset if charset else 'utf-8'
                    decoded_string += part.decode(encoding, errors='replace')
                except (LookupError, UnicodeDecodeError):
                    decoded_string += part.decode('utf-8', errors='replace')
            else:
                decoded_string += str(part)

        return decoded_string.strip()
    except Exception as e:
        logger.warning(f"Failed to decode header value '{header_value}': {e}")
        return str(header_value) if header_value else ""

async def get_access_token(client_id: str, refresh_token: str) -> str:
    """获取访问令牌（支持缓存优化）"""
    # 检查缓存
    cached_token = get_cached_token(client_id, refresh_token)
    if cached_token:
        return cached_token

    token_request_data = {
        'client_id': client_id,
        'grant_type': 'refresh_token',
        'refresh_token': refresh_token,
        'scope': OAUTH_SCOPE
    }

    try:
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(TOKEN_URL, data=token_request_data)
            response.raise_for_status()

            token_data = response.json()
            access_token = token_data.get('access_token')

            if not access_token:
                raise HTTPException(status_code=401, detail="Failed to obtain access token")

            # 缓存Token
            cache_token(client_id, refresh_token, access_token)
            logger.info(f"Successfully obtained and cached access token")
            return access_token

    except httpx.HTTPStatusError as e:
        logger.error(f"HTTP {e.response.status_code} error getting access token: {e}")
        raise HTTPException(status_code=401, detail="Authentication failed")
    except Exception as e:
        logger.error(f"Unexpected error getting access token: {e}")
        raise HTTPException(status_code=500, detail="Token acquisition failed")

def create_imap_connection(email: str, access_token: str) -> imaplib.IMAP4_SSL:
    """创建IMAP连接（与参考项目一致）"""
    try:
        # 设置全局socket超时
        socket.setdefaulttimeout(SOCKET_TIMEOUT)

        # 创建SSL IMAP连接
        imap_client = imaplib.IMAP4_SSL(IMAP_SERVER, IMAP_PORT)

        # 设置连接超时
        imap_client.sock.settimeout(CONNECTION_TIMEOUT)

        # XOAUTH2认证
        auth_string = f"user={email}\x01auth=Bearer {access_token}\x01\x01".encode('utf-8')
        imap_client.authenticate('XOAUTH2', lambda _: auth_string)

        logger.info(f"Successfully created IMAP connection for {email}")
        return imap_client

    except Exception as e:
        logger.error(f"Failed to create IMAP connection for {email}: {e}")
        raise

async def list_emails_sync(email: str, access_token: str, top: int = 20) -> List[EmailMessage]:
    """同步获取邮件列表（在线程池中运行，支持缓存）"""

    # 检查邮件列表缓存
    cached_list = get_cached_email_list(email, top)
    if cached_list:
        return cached_list

    def _sync_list_emails():
        imap_client = None
        try:
            # 使用连接池获取IMAP连接
            imap_client = get_pooled_connection(email, access_token)

            # 选择收件箱
            imap_client.select('INBOX', readonly=True)

            # 优化搜索策略：使用RECENT或限制日期范围
            try:
                # 尝试获取最近的邮件（更快）
                status, messages = imap_client.search(None, "ALL")
                if status != 'OK' or not messages or not messages[0]:
                    return []

                message_ids = messages[0].split()
                total_messages = len(message_ids)

                # 只获取最新的邮件，倒序排列
                message_ids = message_ids[-top:] if total_messages > top else message_ids
                message_ids.reverse()  # 最新的邮件在前

                logger.info(f"Found {total_messages} total emails, fetching latest {len(message_ids)}")

            except Exception as search_error:
                logger.warning(f"Search optimization failed, falling back to basic search: {search_error}")
                status, messages = imap_client.search(None, "ALL")
                if status != 'OK' or not messages or not messages[0]:
                    return []
                message_ids = messages[0].split()[-top:]
                message_ids.reverse()

            message_items = []

            # 优化：逐个获取但使用简化的fetch
            for msg_id in message_ids:
                try:
                    # 只获取必要的头部信息，提高速度
                    status, msg_data = imap_client.fetch(msg_id, '(FLAGS BODY.PEEK[HEADER.FIELDS (SUBJECT DATE FROM)])')

                    if status != 'OK' or not msg_data:
                        continue

                    header_data = msg_data[0][1]
                    msg = email_module.message_from_bytes(header_data)
                    body_preview = ""  # 列表模式下不获取预览，提高速度

                    subject = decode_header_value(msg.get('Subject', '(No Subject)'))
                    from_email = decode_header_value(msg.get('From', '(Unknown Sender)'))
                    date_str = msg.get('Date', '')

                    try:
                        date_obj = parsedate_to_datetime(date_str) if date_str else datetime.now()
                        formatted_date = format_datetime(date_obj.isoformat())
                    except:
                        formatted_date = format_datetime(datetime.now().isoformat())

                    # 提取发件人邮箱和姓名
                    sender_email = from_email
                    sender_name = from_email
                    if '<' in from_email and '>' in from_email:
                        parts = from_email.split('<')
                        if len(parts) == 2:
                            sender_name = parts[0].strip().strip('"')
                            sender_email = parts[1].strip('>')

                    message_item = EmailMessage(
                        id=msg_id.decode(),
                        subject=subject,
                        receivedDateTime=formatted_date,
                        sender={
                            "emailAddress": {
                                "address": sender_email,
                                "name": sender_name
                            }
                        },
                        from_={
                            "emailAddress": {
                                "address": sender_email,
                                "name": sender_name
                            }
                        },
                        body={"content": "", "contentType": "text"},
                        bodyPreview=body_preview or "邮件预览内容"
                    )
                    message_items.append(message_item)

                    logger.info(f"Successfully fetched email: {subject}")

                except Exception as e:
                    logger.warning(f"Failed to fetch email {msg_id}: {e}")
                    continue

            logger.info(f"Successfully retrieved {len(message_items)} emails")
            return message_items

        except Exception as e:
            logger.error(f"Error listing emails: {e}")
            raise HTTPException(status_code=500, detail="Failed to retrieve emails")
        finally:
            # 连接池管理连接，不需要手动关闭
            pass

    # 在线程池中运行同步代码
    result = await asyncio.to_thread(_sync_list_emails)

    # 缓存邮件列表
    cache_email_list(email, top, result)

    return result

async def get_email_detail_sync(email: str, access_token: str, message_id: str) -> dict:
    """同步获取邮件详情（在线程池中运行，支持缓存）"""

    # 检查缓存
    cached_result = get_cached_email(email, message_id)
    if cached_result:
        return cached_result

    def _sync_get_email_detail():
        imap_client = None
        try:
            # 使用连接池获取IMAP连接
            imap_client = get_pooled_connection(email, access_token)

            # 选择收件箱
            imap_client.select('INBOX', readonly=True)

            # 获取完整邮件内容
            status, msg_data = imap_client.fetch(message_id, '(RFC822)')

            if status != 'OK' or not msg_data:
                raise HTTPException(status_code=404, detail="Email not found")

            # 解析邮件
            raw_email = msg_data[0][1]
            msg = email_module.message_from_bytes(raw_email)

            # 提取基本信息
            subject = decode_header_value(msg.get('Subject', '(No Subject)'))
            from_email = decode_header_value(msg.get('From', '(Unknown Sender)'))
            to_email = decode_header_value(msg.get('To', '(Unknown Recipient)'))
            date_str = msg.get('Date', '')

            # 格式化日期
            try:
                if date_str:
                    date_obj = parsedate_to_datetime(date_str)
                    formatted_date = format_datetime(date_obj.isoformat())
                else:
                    formatted_date = format_datetime(datetime.now().isoformat())
            except:
                formatted_date = format_datetime(datetime.now().isoformat())

            # 提取邮件内容与附件
            body_plain = ""
            body_html = ""
            attachments = []
            part_index = 0

            if msg.is_multipart():
                for part in msg.walk():
                    content_type = part.get_content_type()
                    content_disposition = str(part.get("Content-Disposition", ""))
                    filename = part.get_filename()

                    # 识别附件（包含 attachment 或者存在文件名）
                    is_attachment = ('attachment' in content_disposition.lower()) or bool(filename)

                    if is_attachment:
                        try:
                            payload = part.get_payload(decode=True) or b''
                            size = len(payload)
                            decoded_filename = decode_header_value(filename) if filename else f"attachment-{part_index}"
                            attachments.append({
                                "index": part_index,
                                "filename": decoded_filename,
                                "contentType": content_type,
                                "size": size,
                            })
                        except Exception as e:
                            logger.warning(f"Failed to read attachment part: {e}")
                    else:
                        try:
                            charset = part.get_content_charset() or 'utf-8'
                            payload = part.get_payload(decode=True)

                            if payload:
                                decoded_content = payload.decode(charset, errors='replace')

                                if content_type == 'text/plain' and not body_plain:
                                    body_plain = decoded_content
                                elif content_type == 'text/html' and not body_html:
                                    body_html = decoded_content
                        except Exception as e:
                            logger.warning(f"Failed to decode email part: {e}")

                    part_index += 1
            else:
                try:
                    charset = msg.get_content_charset() or 'utf-8'
                    payload = msg.get_payload(decode=True)

                    if payload:
                        content = payload.decode(charset, errors='replace')
                        content_type = msg.get_content_type()

                        if content_type == 'text/plain':
                            body_plain = content
                        elif content_type == 'text/html':
                            body_html = content
                        else:
                            body_plain = content
                except Exception as e:
                    logger.warning(f"Failed to decode single-part email: {e}")

            # 提取发件人邮箱和姓名
            sender_email = from_email
            sender_name = from_email
            if '<' in from_email and '>' in from_email:
                parts = from_email.split('<')
                if len(parts) == 2:
                    sender_name = parts[0].strip().strip('"')
                    sender_email = parts[1].strip('>')

            result = {
                "id": message_id,
                "subject": subject,
                "from": from_email,
                "to": to_email,
                "receivedDateTime": formatted_date,
                "sender": {
                    "emailAddress": {
                        "address": sender_email,
                        "name": sender_name
                    }
                },
                "body": {
                    "content": body_html or body_plain or "无邮件内容",
                    "contentType": "html" if body_html else "text"
                },
                "attachments": attachments,
                "bodyPreview": (body_html or body_plain or "")[:200] + "..." if (body_html or body_plain) else ""
            }

            return result

        except Exception as e:
            logger.error(f"Error getting email detail: {e}")
            raise HTTPException(status_code=500, detail="Failed to retrieve email details")
        finally:
            # 连接池管理连接，不需要手动关闭
            pass

    # 在线程池中运行同步代码并缓存
    result = await asyncio.to_thread(_sync_get_email_detail)
    cache_email(email, message_id, result)
    return result

    # 在线程池中运行同步代码

@app.post("/api/temp-message-detail", response_model=EmailDetailResponse)
async def get_temp_message_detail(request: TempMessageDetailRequest):
    """获取临时账户的邮件详情（供前端移动端/临时账户模式使用）"""
    try:
        logger.info(f"Getting temp email detail for message: {request.message_id}")
        # 获取访问令牌
        access_token = await get_access_token(request.client_id, request.refresh_token)
        # 获取详情
        detail = await get_email_detail_sync(request.email, access_token, request.message_id)
        if not isinstance(detail, dict):
            logger.warning("Email detail is empty or invalid, returning empty object")
            detail = {}
        return EmailDetailResponse(success=True, message="", data=detail)
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error in get_temp_message_detail: {e}")
        raise HTTPException(status_code=500, detail=str(e))



@app.post("/api/temp-messages", response_model=TempMessageResponse)
async def get_temp_messages(request: TempMessageRequest):
    """获取临时邮件（与前端兼容的接口）"""
    try:
        logger.info(f"Getting emails for: {request.email}")

        # 获取访问令牌
        access_token = await get_access_token(request.client_id, request.refresh_token)

        # 获取邮件列表
        messages = await list_emails_sync(request.email, access_token, request.top)

        logger.info(f"Successfully retrieved {len(messages)} emails")

        return TempMessageResponse(
            success=True,
            message="",
            data=messages
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error in get_temp_messages: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/api/message/{message_id}", response_model=EmailDetailResponse)
async def get_message_detail(message_id: str, email: str, client_id: str, refresh_token: str):
    """获取邮件详细内容"""
    try:
        logger.info(f"Getting email detail for message: {message_id}")

        # 获取访问令牌
        access_token = await get_access_token(client_id, refresh_token)

        # 获取邮件详情
        detail = await get_email_detail_sync(email, access_token, message_id)

        logger.info(f"Successfully retrieved email detail for: {message_id}")

        return EmailDetailResponse(
            success=True,
            message="",
            data=detail
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error in get_message_detail: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/api/health")
async def health_check():
    """健康检查"""
    return {
        "success": True,
        "message": "FastAPI邮件服务器运行正常",
        "timestamp": datetime.now().isoformat()
    }

@app.get("/styles.css")
async def get_styles():
    """返回样式文件"""
    return FileResponse("styles.css")

@app.get("/app.js")
async def get_app_js():
    """返回应用脚本"""
    return FileResponse("app.js")

@app.get("/api.js")
async def get_api_js():
    """返回API脚本"""
    return FileResponse("api.js")

@app.get("/serviceWorker.js")
async def get_service_worker():
    """返回Service Worker"""
    return FileResponse("serviceWorker.js")

@app.get("/")
async def root():
    """根路径 - 返回前端页面（指向 test/111.html）"""
    return FileResponse("test/111.html")

@app.get("/111.css")
async def get_demo_css():
    return FileResponse("test/111.css")

@app.get("/111.js")
async def get_demo_js():
    return FileResponse("test/111.js")


if __name__ == "__main__":
    import uvicorn
    import os

    # 切换到项目根目录
    os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

    logger.info("Starting FastAPI Email Server...")
    logger.info("Based on OutlookManager-main/main.py implementation")
    logger.info(f"Working directory: {os.getcwd()}")

    uvicorn.run(
        app,
        host="127.0.0.1",
        port=3000,
        log_level="info"
    )
